home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1993 July / InfoMagic USENET CD-ROM July 1993.ISO / sources / misc / volume16 / strftime / part01 next >
Encoding:
Internet Message Format  |  1991-02-07  |  13.7 KB

  1. From: arnold@audiofax.com (Arnold Robbins)
  2. Newsgroups: comp.sources.misc
  3. Subject: v16i094:  Implementation of ANSI strftime routine, Part01/01
  4. Message-ID: <1991Feb7.184407.16023@sparky.IMD.Sterling.COM>
  5. Date: 7 Feb 91 18:44:07 GMT
  6. Approved: kent@sparky.imd.sterling.com
  7. X-Checksum-Snefru: 33c79f34 2fbcc87b 81890abe fa95736b
  8.  
  9. Submitted-by: arnold@audiofax.com (Arnold Robbins)
  10. Posting-number: Volume 16, Issue 94
  11. Archive-name: strftime/part01
  12.  
  13. I happened recently to have need of the ANSI strftime routine. Only our
  14. system didn't have it (since it's not an ANSI system).  Here is a
  15. public domain implementation for System V systems.  You do need to have
  16. a version of the ANSI mktime routine for this to work.
  17.  
  18. Enjoy,
  19.  
  20. Arnold Robbins                AudioFAX, Inc. | Laundry increases
  21. 2000 Powers Ferry Road, #200 / Marietta, GA. 30067     | exponentially in the
  22. INTERNET: arnold@audiofax.com Phone:   +1 404 933 7600 | number of children.
  23. UUCP:      emory!audfax!arnold Fax-box: +1 404 618 4581 |   -- Miriam Robbins
  24. ----------------- cut here ----------------------------------------------
  25. #! /bin/sh
  26. # This is a shell archive, meaning:
  27. # 1. Remove everything above the #! /bin/sh line.
  28. # 2. Save the resulting text in a file.
  29. # 3. Execute the file with /bin/sh (not csh) to create:
  30. #    strftime.c
  31. #    strftime.3
  32. # This archive created: Wed Feb  6 13:39:23 1991
  33. export PATH; PATH=/bin:/usr/bin:$PATH
  34. echo shar: "extracting 'strftime.c'" '(6727 characters)'
  35. if test -f 'strftime.c'
  36. then
  37.     echo shar: "will not over-write existing file 'strftime.c'"
  38. else
  39. cat << \SHAR_EOF > 'strftime.c'
  40. /*
  41.  * strftime.c
  42.  *
  43.  * Public-domain relatively quick-and-dirty implemenation of
  44.  * ANSI library routine for System V Unix systems.
  45.  *
  46.  * It's written in old-style C for maximal portability.
  47.  * However, since I'm used to prototypes, I've included them too.
  48.  *
  49.  * If you want stuff in the System V ascftime routine, add the SYSV_EXT define.
  50.  *
  51.  * The code for %c, %x, and %X is my best guess as to what's "appropriate".
  52.  * This version ignores LOCALE information.
  53.  * It also doesn't worry about multi-byte characters.
  54.  * So there.
  55.  *
  56.  * Arnold Robbins
  57.  * January, February, 1991
  58.  */
  59.  
  60. #include <stdio.h>
  61. #include <string.h>
  62. #include <time.h>
  63. #include <sys/types.h>
  64.  
  65. #ifndef __STDC__
  66. #define const    /**/
  67. #endif
  68.  
  69. #ifndef __STDC__
  70. extern time_t mktime();
  71. extern void tzset();
  72. extern int abs();
  73. extern char *strchr();
  74. static int weeknumber();
  75. #else
  76. extern time_t mktime(struct tm *then);
  77. extern void tzset(void);
  78. extern int abs(int val);
  79. extern char *strchr(const char *str, int ch);
  80. static int weeknumber(const struct tm *timeptr, int firstweekday);
  81. #endif
  82.  
  83. extern char *tzname[2];
  84. extern int daylight;
  85.  
  86. #define SYSV_EXT    1    /* stuff in System V ascftime routine */
  87.  
  88. /* strftime --- produce formatted time */
  89.  
  90. #ifndef __STDC__
  91. size_t
  92. strftime(s, maxsize, format, timeptr)
  93. char *s;
  94. size_t maxsize;
  95. const char *format;
  96. const struct tm *timeptr;
  97. #else
  98. size_t
  99. strftime(char *s, size_t maxsize, const char *format, const struct tm *timeptr)
  100. #endif
  101. {
  102.     char *endp = s + maxsize;
  103.     char *start = s;
  104.     char tbuf[100];
  105.     int i;
  106.  
  107.     /* various tables, useful in North America */
  108.     static char *days_a[] = {
  109.         "Sun", "Mon", "Tue", "Wed",
  110.         "Thu", "Fri", "Sat",
  111.     };
  112.     static char *days_l[] = {
  113.         "Sunday", "Monday", "Tuesday", "Wednesday",
  114.         "Thursday", "Friday", "Saturday",
  115.     };
  116.     static char *months_a[] = {
  117.         "Jan", "Feb", "Mar", "Apr", "May", "Jun",
  118.         "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
  119.     };
  120.     static char *months_l[] = {
  121.         "January", "February", "March", "April",
  122.         "May", "June", "July", "August", "September",
  123.         "October", "November", "December",
  124.     };
  125.     static char *ampm[] = { "AM", "PM", };
  126.  
  127.     if (s == NULL || format == NULL || timeptr == NULL || maxsize == 0)
  128.         return 0;
  129.  
  130.     if (strchr(format, '%') == NULL && strlen(format) + 1 >= maxsize)
  131.         return 0;
  132.  
  133.     tzset();
  134.  
  135.     for (; *format && s < endp - 1; format++) {
  136.         tbuf[0] = '\0';
  137.         if (*format != '%') {
  138.             *s++ = *format;
  139.             continue;
  140.         }
  141.         switch (*++format) {
  142.         case '\0':
  143.             *s++ = '%';
  144.             goto out;
  145.  
  146.         case '%':
  147.             *s++ = '%';
  148.             continue;
  149.  
  150.         case 'a':    /* abbreviated weekday name */
  151.             strcpy(tbuf, days_a[timeptr->tm_wday]);
  152.             break;
  153.  
  154.         case 'A':    /* full weekday name */
  155.             strcpy(tbuf, days_l[timeptr->tm_wday]);
  156.             break;
  157.  
  158. #ifdef SYSV_EXT
  159.         case 'h':    /* abbreviated month name */
  160. #endif
  161.         case 'b':    /* abbreviated month name */
  162.             strcpy(tbuf, months_a[timeptr->tm_mon]);
  163.             break;
  164.  
  165.         case 'B':    /* full month name */
  166.             strcpy(tbuf, months_l[timeptr->tm_mon]);
  167.             break;
  168.  
  169.         case 'c':    /* appropriate date and time representation */
  170.             sprintf(tbuf, "%s %s %2d %02d:%02d:%02d %d",
  171.                 days_a[timeptr->tm_wday],
  172.                 months_a[timeptr->tm_mon],
  173.                 timeptr->tm_mday,
  174.                 timeptr->tm_hour,
  175.                 timeptr->tm_min,
  176.                 timeptr->tm_sec,
  177.                 timeptr->tm_year + 1900);
  178.             break;
  179.  
  180.         case 'd':    /* day of the month, 01 - 31 */
  181.             sprintf(tbuf, "%02d", timeptr->tm_mday);
  182.             break;
  183.  
  184.         case 'H':    /* hour, 24-hour clock, 00 - 23 */
  185.             sprintf(tbuf, "%02d", timeptr->tm_hour);
  186.             break;
  187.  
  188.         case 'I':    /* hour, 12-hour clock, 01 - 12 */
  189.             i = timeptr->tm_hour;
  190.             if (i == 0)
  191.                 i = 12;
  192.             else if (i > 12)
  193.                 i -= 12;
  194.             sprintf(tbuf, "%02d", i);
  195.             break;
  196.  
  197.         case 'j':    /* day of the year, 001 - 366 */
  198.             sprintf(tbuf, "%03d", timeptr->tm_yday + 1);
  199.             break;
  200.  
  201.         case 'm':    /* month, 01 - 12 */
  202.             sprintf(tbuf, "%02d", timeptr->tm_mon + 1);
  203.             break;
  204.  
  205.         case 'M':    /* minute, 00 - 59 */
  206.             sprintf(tbuf, "%02d", timeptr->tm_min);
  207.             break;
  208.  
  209.         case 'p':    /* am or pm based on 12-hour clock */
  210.             if (timeptr->tm_hour < 12)
  211.                 strcpy(tbuf, ampm[0]);
  212.             else
  213.                 strcpy(tbuf, ampm[1]);
  214.             break;
  215.  
  216.         case 'S':    /* second, 00 - 61 */
  217.             sprintf(tbuf, "%02d", timeptr->tm_sec);
  218.             break;
  219.  
  220.         case 'U':    /* week of year, Sunday is first day of week */
  221.             sprintf(tbuf, "%d", weeknumber(timeptr, 0));
  222.             break;
  223.  
  224.         case 'w':    /* weekday, Sunday == 0, 0 - 6 */
  225.             sprintf(tbuf, "%d", timeptr->tm_wday);
  226.             break;
  227.  
  228.         case 'W':    /* week of year, Monday is first day of week */
  229.             sprintf(tbuf, "%d", weeknumber(timeptr, 1));
  230.             break;
  231.  
  232.         case 'x':    /* appropriate date representation */
  233.             sprintf(tbuf, "%s %s %2d %d",
  234.                 days_a[timeptr->tm_wday],
  235.                 months_a[timeptr->tm_mon],
  236.                 timeptr->tm_mday,
  237.                 timeptr->tm_year + 1900);
  238.             break;
  239.  
  240.         case 'X':    /* appropriate time representation */
  241.             sprintf(tbuf, "%02d:%02d:%02d",
  242.                 timeptr->tm_hour,
  243.                 timeptr->tm_min,
  244.                 timeptr->tm_sec);
  245.             break;
  246.  
  247.         case 'y':    /* year without a century, 00 - 99 */
  248.             i = timeptr->tm_year % 100;
  249.             sprintf(tbuf, "%d", i);
  250.             break;
  251.  
  252.         case 'Y':    /* year with century */
  253.             sprintf(tbuf, "%d", 1900 + timeptr->tm_year);
  254.             break;
  255.  
  256.         case 'Z':    /* time zone name or abbrevation */
  257.             i = 0;
  258.             if (daylight && timeptr->tm_isdst)
  259.                 i = 1;
  260.             strcpy(tbuf, tzname[i]);
  261.             break;
  262.  
  263. #ifdef SYSV_EXT
  264.         case 'n':    /* same as \n */
  265.             tbuf[0] = '\n';
  266.             tbuf[1] = '\0';
  267.             break;
  268.  
  269.         case 't':    /* same as \t */
  270.             tbuf[0] = '\t';
  271.             tbuf[1] = '\0';
  272.             break;
  273.  
  274.         case 'D':    /* date as %m/%d/%y */
  275.             strftime(tbuf, sizeof tbuf, "%m/%d/%y", timeptr);
  276.             break;
  277.  
  278.         case 'e':    /* day of month, blank padded */
  279.             sprintf(tbuf, "%2d", timeptr->tm_mday);
  280.             break;
  281.  
  282.         case 'r':    /* time as %I:%M:%S %p */
  283.             strftime(tbuf, sizeof tbuf, "%I:%M:%S %p", timeptr);
  284.             break;
  285.  
  286.         case 'R':    /* time as %H:%M */
  287.             strftime(tbuf, sizeof tbuf, "%H:%M", timeptr);
  288.             break;
  289.  
  290.         case 'T':    /* time as %H:%M:%S */
  291.             strftime(tbuf, sizeof tbuf, "%H:%M:%S", timeptr);
  292.             break;
  293. #endif
  294.  
  295.         default:
  296.             tbuf[0] = '%';
  297.             tbuf[1] = *format;
  298.             tbuf[2] = '\0';
  299.             break;
  300.         }
  301.         i = strlen(tbuf);
  302.         if (i)
  303.             if (s + i < endp - 1) {
  304.                 strcpy(s, tbuf);
  305.                 s += i;
  306.             } else
  307.                 return 0;
  308.     }
  309. out:
  310.     if (s < endp && *format == '\0') {
  311.         *s = '\0';
  312.         return (s - start);
  313.     } else
  314.         return 0;
  315. }
  316.  
  317. /* weeknumber --- figure how many weeks into the year */
  318.  
  319. #ifndef __STDC__
  320. static int
  321. weeknumber(timeptr, firstweekday)
  322. const struct tm *timeptr;
  323. int firstweekday;
  324. #else
  325. static int
  326. weeknumber(const struct tm *timeptr, int firstweekday)
  327. #endif
  328. {
  329.     struct tm t1;
  330.     time_t then;
  331.     long ydays;
  332.  
  333.     /* find out what day of the week january 1 was */
  334.     t1 = *timeptr;
  335.     t1.tm_yday = 0;
  336.     t1.tm_mon = 0;
  337.     t1.tm_mday = 1;
  338.     then = mktime(& t1);
  339.     t1 = *localtime(& then);
  340.  
  341.     /* use that info to normalize the week count */
  342.     ydays = timeptr->tm_yday + t1.tm_wday + firstweekday - 1;
  343.     return ydays / 7;
  344. }
  345. SHAR_EOF
  346. fi
  347. echo shar: "extracting 'strftime.3'" '(4976 characters)'
  348. if test -f 'strftime.3'
  349. then
  350.     echo shar: "will not over-write existing file 'strftime.3'"
  351. else
  352. cat << \SHAR_EOF > 'strftime.3'
  353. .TH STRFTIME 3
  354. .SH NAME
  355. strftime \- generate formatted time information
  356. .SH SYNOPSIS
  357. .ft B
  358. .nf
  359. #include <sys/types.h>
  360. #include <time.h>
  361. .sp
  362. size_t strftime(char *s, size_t maxsize, const char *format,
  363.     const struct tm *timeptr);
  364. .SH DESCRIPTION
  365. The following description is transcribed verbatim from the December 7, 1988
  366. draft standard for ANSI C.
  367. This draft is essentially identical in technical content
  368. to the final version of the standard.
  369. .LP
  370. The
  371. .B strftime
  372. function places characters into the array pointed to by
  373. .B s
  374. as controlled by the string pointed to by
  375. .BR format .
  376. The format shall be a multibyte character sequence, beginning and ending in
  377. its initial shift state.
  378. The
  379. .B format
  380. string consists of zero or more conversion specifiers and ordinary
  381. multibyte characters.  A conversion specifier consists of a
  382. .B %
  383. character followed by a character that determines the behavior of the
  384. conversion specifier.
  385. All ordinary multibyte characters (including the terminating null
  386. character) are copied unchanged into the array.
  387. If copying takes place between objects that overlap the behavior is
  388. undefined.
  389. No more than
  390. .B maxsize
  391. characters are placed into the array.
  392. Each conversion specifier is replaced by appropriate characters as described
  393. in the following list.
  394. The appropriate characters are determined by the
  395. .B LC_TIME
  396. category of the current locale and by the values contained in the
  397. structure pointed to by
  398. .BR timeptr .
  399. .TP
  400. .B %a
  401. is replaced by the locale's abbreviated weekday name.
  402. .TP
  403. .B %A
  404. is replaced by the locale's full weekday name.
  405. .TP
  406. .B %b
  407. is replaced by the locale's abbreviated month name.
  408. .TP
  409. .B %B
  410. is replaced by the locale's full month name.
  411. .TP
  412. .B %c
  413. is replaced by the locale's appropriate date and time representation.
  414. .TP
  415. .B %d
  416. is replaced by the day of the month as a decimal number
  417. .RB ( 01 - 31 ).
  418. .TP
  419. .B %H
  420. is replaced by the hour (24-hour clock) as a decimal number
  421. .RB ( 00 - 23 ).
  422. .TP
  423. .B %I
  424. is replaced by the hour (12-hour clock) as a decimal number
  425. .RB ( 01 - 12 ).
  426. .TP
  427. .B %j
  428. is replaced by the day of the year as a decimal number
  429. .RB ( 001 - 366 ).
  430. .TP
  431. .B %m
  432. is replaced by the month as a decimal number
  433. .RB ( 01 - 12 ).
  434. .TP
  435. .B %M
  436. is replaced by the minute as a decimal number
  437. .RB ( 00 - 59 ).
  438. .TP
  439. .B %p
  440. is replaced by the locale's equivalent of the AM/PM designations associated
  441. with a 12-hour clock.
  442. .TP
  443. .B %S
  444. is replaced by the second as a decimal number
  445. .RB ( 00 - 61 ).
  446. .TP
  447. .B %U
  448. is replaced by the week number of the year (the first Sunday as the first
  449. day of week 1) as a decimal number
  450. .RB ( 00 - 53 ).
  451. .TP
  452. .B %w
  453. is replaced by the weekday as a decimal number
  454. .RB [ "0 " (Sunday)- 6 ].
  455. .TP
  456. .B %W
  457. is replaced by the week number of the year (the first Monday as the first
  458. day of week 1) as a decimal number
  459. .RB ( 00 - 53 ).
  460. .TP
  461. .B %x
  462. is replaced by the locale's appropriate date representation.
  463. .TP
  464. .B %X
  465. is replaced by the locale's appropriate time representation.
  466. .TP
  467. .B %y
  468. is replaced by the year without century as a decimal number
  469. .RB ( 00 - 99 ).
  470. .TP
  471. .B %Y
  472. is replaced by the year with century as a decimal number.
  473. .TP
  474. .B %z
  475. is replaced by the time zone name or abbreviation, or by no characters if
  476. no time zone is determinable.
  477. .TP
  478. .B %%
  479. is replaced by
  480. .BR % .
  481. .LP
  482. If a conversion specifier is not one of the above, the behavior is
  483. undefined.
  484. .SH RETURNS
  485. If the total number of resulting characters including the terminating null
  486. character is not more than
  487. .BR maxsize ,
  488. the
  489. .B strftime
  490. function returns the number of characters placed into the array pointed to
  491. by
  492. .B s
  493. not including the terminating null character.
  494. Otherwise, zero is returned and the contents of the array are indeterminate.
  495. .SH NON-ANSI EXTENSIONS
  496. If
  497. .B SYSV_EXT
  498. is defined when the routine is compiled, then the following additional
  499. conversions will be available.
  500. These are borrowed from the System V
  501. .IR cftime (3)
  502. and
  503. .IR ascftime (3)
  504. routines.
  505. .TP
  506. .B %D
  507. is equivalent to specifying
  508. .BR %m/%d/%y .
  509. .TP
  510. .B %e
  511. is replaced by the day of the month,
  512. padded with a blank if it is only one digit.
  513. .TP
  514. .B %h
  515. is equivalent to
  516. .BR %b ,
  517. above.
  518. .TP
  519. .B %n
  520. is replaced with a newline character (\s-1ASCII LF\s+1).
  521. .TP
  522. .B %r
  523. is equivalent to specifying
  524. .BR "%I:%M:%S %p" .
  525. .TP
  526. .B %R
  527. is equivalent to specifying
  528. .BR %H:%M: .
  529. .TP
  530. .B %T
  531. is equivalent to specifying
  532. .BR %H:%M:%S .
  533. .TP
  534. .B %t
  535. is replaced with a \s-1TAB\s+1 character.
  536. .SH SEE ALSO
  537. time(2), ctime(3), localtime(3)
  538. .SH BUGS
  539. This version does not handle multibyte characters or pay attention to the
  540. setting of the
  541. .B LC_TIME
  542. environment variable.
  543. .LP
  544. It is not clear what is ``appropriate'' for the C locale; the values
  545. returned are a best guess on the author's part.
  546. .SH AUTHOR
  547. .nf
  548. Arnold Robbins
  549. AudioFAX, Inc.
  550. Suite 200
  551. 2000 Powers Ferry Road
  552. Marietta, GA. 30067
  553. U.S.A.
  554. INTERNET: arnold@audiofax.com
  555. UUCP:     emory!audfax!arnold
  556. Phone:    +1 404 933 7600
  557. Fax-box:  +1 404 618 4581
  558. .fi
  559. .SH ACKNOWLEDGEMENTS
  560. Thanks to Geoff Clare <gwc@root.co.uk> for helping debug earlier
  561. versions of this routine.
  562. SHAR_EOF
  563. fi
  564. exit 0
  565. #    End of shell archive
  566. -- 
  567. Arnold Robbins                AudioFAX, Inc. | Laundry increases
  568. 2000 Powers Ferry Road, #200 / Marietta, GA. 30067     | exponentially in the
  569. INTERNET: arnold@audiofax.com Phone:   +1 404 933 7612 | number of children.
  570. UUCP:      emory!audfax!arnold Fax-box: +1 404 618 4581 |   -- Miriam Robbins
  571.  
  572. exit 0 # Just in case...
  573. -- 
  574. Kent Landfield                   INTERNET: kent@sparky.IMD.Sterling.COM
  575. Sterling Software, IMD           UUCP:     uunet!sparky!kent
  576. Phone:    (402) 291-8300         FAX:      (402) 291-4362
  577. Please send comp.sources.misc-related mail to kent@uunet.uu.net.
  578.